package com.hitqz.robot.dispatch.netty.service.handle.impl;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.cache.Cache;
import com.google.common.collect.Maps;
import com.hitqz.robot.api.business.dto.CmdRobot2PosDto;
import com.hitqz.robot.api.business.enums.NavEnum;
import com.hitqz.robot.api.common.dto.info.RobotNavInfoDto;
import com.hitqz.robot.api.common.entity.RobotMapPos;
import com.hitqz.robot.biz.mapper.PathResourceMapper;
import com.hitqz.robot.biz.service.JobModeService;
import com.hitqz.robot.dispatch.netty.service.handle.IRuleHandle;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author kehanjiang
 * @date 2023/11/03
 */
@Slf4j
@Component
public abstract class AbstractRuleHandle implements IRuleHandle, SmartInitializingSingleton {

    @Resource
    protected PathResourceMapper pathResourceMapper;

    @Autowired
    protected RestTemplate restTemplate;

    @Autowired
    protected JobModeService jobModeService;

    protected ThreadPoolExecutor threadPoolExecutor;

    protected Map<String, List<FutureTask>> futureTaskMap = new HashMap<>();

    @Value("${robot.client-address1}")
    protected String clientAddress1;
    @Value("${robot.client-address2}")
    protected String clientAddress2;

    private String[] urls = {};
    private int index = 0;

    /**
     * 轮询地址
     *
     * @return
     */
    protected String getRoundRobinUrl() {
        String url = urls[index];
        index = (index + 1) % urls.length; // 轮询
        return url;
    }

    @Override
    public void afterSingletonsInstantiated() {
        this.threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(50));
        this.urls = new String[]{clientAddress1, clientAddress2};
    }

    /**
     * 查询导航路径
     *
     * @param clientAddress
     * @param sourceCode
     * @param targetCode
     * @return
     */
    protected List<RobotMapPos> getNavPath(String clientAddress, String sourceCode, String targetCode) {
        try {
            String url = clientAddress + "/ctrRobot/getNavPathWithCode?sourceCode={param1}&targetCode={param2}";
            Map<String, String> params = new HashMap<>();
            params.put("param1", sourceCode);
            params.put("param2", targetCode);
            String response = restTemplate.getForObject(url, String.class, params);
            List<RobotMapPos> list = JSONUtil.toList(JSONUtil.parseArray(response), RobotMapPos.class);
            List<String> posList = list.stream().map(RobotMapPos::getCode).toList();
            while (!sourceCode.equals(posList.get(0)) || !targetCode.equals(posList.get(posList.size() - 1))) {
                log.warn("getNavPath返回结果错误！sourceCode：{} targetCode：{} 返回路径：{}", sourceCode, targetCode, posList);
                TimeUnit.SECONDS.sleep(1);
                response = restTemplate.getForObject(url, String.class, params);
                list = JSONUtil.toList(JSONUtil.parseArray(response), RobotMapPos.class);
                posList = list.stream().map(RobotMapPos::getCode).toList();
                log.warn("重新获取结果:{}", posList);
            }
            return list;
        } catch (Exception e) {
            log.error("查询导航路径失败！sourceCode: {}, targetCode: {}  ,error: {}", sourceCode, targetCode, e.getMessage());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException ex) {
            }
            log.warn("重新获取请求getNavPath!");
            return getNavPath(clientAddress, sourceCode, targetCode);
        }
    }

    /**
     * AGV当前无法申请到 资源ID
     */
    protected static Map<String, Long> blockForApplyResource = Maps.newConcurrentMap();

    /**
     * 当前是否两车相互等待申请彼此所占资源
     *
     * @return
     */
    protected boolean allAgvBlockForApplyResource() {
        return blockForApplyResource.values().stream()
                .filter(x -> x.doubleValue() > 0)
                .collect(Collectors.toList()).size() == 2;
    }

    @Override
    public void cancelGotoTarget(String robotSn) {
        List<FutureTask> futureTaskList = Optional.ofNullable(futureTaskMap.get(robotSn))
                .orElse(new ArrayList<>());
        for (FutureTask futureTask : futureTaskList) {
            try {
                if (!futureTask.isDone()) {
                    log.warn("取消{} 的调度前往任务!", robotSn);
                    futureTask.cancel(true);
                }
            } catch (Exception e) {
                log.error("{}取消导航任务失败！ {}", robotSn, e);
            }
        }
        blockForApplyResource.put(robotSn, -1L);
    }

    /**
     * 当前AGV点位 （一定有值）
     *
     * @param clientId
     * @return
     * @throws Exception
     */
    protected String getCurrentAgvPos(String clientId) throws Exception {
        RobotNavInfoDto currentRobotNavInfoDto = this.getCurrentAgvDto(clientId);
        String current = currentRobotNavInfoDto.getCurrentPos() == null ? "" : currentRobotNavInfoDto.getCurrentPos().getCode();
        while (!StringUtils.hasText(current)) {
            currentRobotNavInfoDto = this.getCurrentAgvDto(clientId);
            current = currentRobotNavInfoDto.getCurrentPos() == null ? "" : currentRobotNavInfoDto.getCurrentPos().getCode();
            TimeUnit.SECONDS.sleep(1);
        }
        return current;
    }

    /**
     * 其他AGV点位 （一定有值）
     *
     * @param clientId
     * @return
     * @throws Exception
     */
    protected String getOtherAgvPos(String clientId) throws Exception {
        RobotNavInfoDto otherRobotNavInfoDto = this.getOtherAgvDto(clientId);
        String current = otherRobotNavInfoDto.getCurrentPos() == null ? "" : otherRobotNavInfoDto.getCurrentPos().getCode();
        while (!StringUtils.hasText(current)) {
            otherRobotNavInfoDto = this.getOtherAgvDto(clientId);
            current = otherRobotNavInfoDto.getCurrentPos() == null ? "" : otherRobotNavInfoDto.getCurrentPos().getCode();
            TimeUnit.SECONDS.sleep(1);
        }
        return current;
    }

    /**
     * 当前AGV导航信息
     *
     * @param clientId
     * @return
     */
    protected RobotNavInfoDto getCurrentAgvDto(String clientId) {
        Cache<String, RobotNavInfoDto> robotNavInfoDtoMapCache = SpringUtil.getBean("robotNavInfo");
        return robotNavInfoDtoMapCache.getIfPresent(clientId);
    }

    /**
     * 其他AGV导航信息
     *
     * @param clientId
     * @return
     */
    protected RobotNavInfoDto getOtherAgvDto(String clientId) {
        Cache<String, RobotNavInfoDto> robotNavInfoDtoMapCache = SpringUtil.getBean("robotNavInfo");
        RobotNavInfoDto otherRobotNavInfoDto = robotNavInfoDtoMapCache.asMap().values()
                .stream().filter(dto -> !dto.getRobotSn().equals(clientId)).findFirst().orElse(null);
        return otherRobotNavInfoDto;
    }

    /**
     * 机器人前往点位
     *
     * @param robotSn
     * @param posCode
     */
    @SneakyThrows
    protected void cmdRobot2Pos(String robotSn, String posCode) {
        String currentPos = this.getCurrentAgvPos(robotSn);
        if (posCode.equals(currentPos)) {
            log.warn("机器人已在即将前往的目标位置{}，忽略下发前往！", posCode);
        } else {
            CmdRobot2PosDto dto = new CmdRobot2PosDto();
            dto.setClientId(robotSn);
            dto.setPosCode(posCode);
            SpringUtil.getApplicationContext().publishEvent(dto);
        }
    }
}